package com.spark.modules.utils;

import com.spark.modules.generation.entity.model.TemplateModel;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 代码生成器工具类，加载模板并生成文件。
 *
 * @author LYCHEE
 * @date 2025/2/17 09:53
 */
@Slf4j
public class CodeGeneratorUtils {

    private final Configuration freemarkerConfig;

    public CodeGeneratorUtils() {
        // 初始化 FreeMarker 配置
        freemarkerConfig = new Configuration(Configuration.VERSION_2_3_31);
        freemarkerConfig.setDefaultEncoding("UTF-8");
    }

    /**
     * 动态设置模板加载路径
     *
     * @param templatePath 模板文件所在的路径
     */
    public void setTemplateLoaderPath(String templatePath) {
        try {
            freemarkerConfig.setClassLoaderForTemplateLoading(this.getClass().getClassLoader(), "templates" + templatePath);
        } catch (Exception e) {
            log.error("Failed to set template loader path to: {}", templatePath, e);
        }
    }

    /**
     * 生成代码文件
     *
     * @param templateName 模板文件名（如 "controller.ftl"）
     * @param outputPath   输出文件路径
     * @param data         数据模型
     */
    public void generateFile(String templateName, String outputPath, Map<String, Object> data) {
        try {
            // 加载模板
            Template template = freemarkerConfig.getTemplate(templateName);

            // 生成代码文件
            File outputFile = new File(outputPath);
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }
            Writer fileWriter = new FileWriter(outputPath);
            template.process(data, fileWriter);
            fileWriter.close();

            log.info("文件生成成功：{}", outputPath);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试预览代码
     */
    public String previewCode(Map<String, Object> params, String templateStr) {
        try {
            Template template = freemarkerConfig.getTemplate(templateStr);
            StringWriter stringWriter = new StringWriter();
            template.process(params, stringWriter);
            return stringWriter.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "Error during code generation";
        }
    }

    /**
     * 新增生成ZIP的方法
     *
     * @param outputStream
     * @throws IOException
     */
    public void generateZip(OutputStream outputStream, Map<String, TemplateModel> template, Map<String, Object> templateParams, String tagPath) throws IOException {
        Path tempDir = Files.createTempDirectory("codegen");
        try {
            // 示例：生成多个文件到临时目录
            for (Map.Entry<String, TemplateModel> entry : template.entrySet()) {
                String key = entry.getKey();
                TemplateModel value = entry.getValue();
                setTemplateLoaderPath(value.getTemplatePath());
                String genPath = tagPath.concat(value.getTargetPath()).concat("/");
                if (StringUtils.isNotEmpty(value.getPrefix())) {
                    genPath = genPath.concat(value.getPrefix());
                }
                genPath = genPath.concat((String) templateParams.get("ClassName")).concat(value.getSuffix());
                // 生成文件
                generateFile(
                        key,
                        tempDir.resolve(genPath).toString(),
                        templateParams
                );
            }
            // 压缩临时目录到输出流
            try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
                Files.walk(tempDir)
                        .filter(path -> !Files.isDirectory(path))
                        .forEach(path -> {
                            try {
                                String entryName = tempDir.relativize(path).toString().replace(File.separatorChar, '/');
                                zipOut.putNextEntry(new ZipEntry(entryName));
                                Files.copy(path, zipOut);
                                zipOut.closeEntry();
                            } catch (IOException e) {
                                throw new UncheckedIOException(e);
                            }
                        });
            } catch (UncheckedIOException e) {
                throw e.getCause();
            }
        } finally {
            // 删除临时目录
            FileUtils.deleteDirectory(tempDir.toFile());
        }
    }
}
